Skip to content

Fix Windows-specific cobj.exe bugs (#828) - #862

Open
yutaro-sakamoto wants to merge 7 commits into
opensourcecobol:developfrom
yutaro-sakamoto:fix-windows-bugs-828
Open

Fix Windows-specific cobj.exe bugs (#828)#862
yutaro-sakamoto wants to merge 7 commits into
opensourcecobol:developfrom
yutaro-sakamoto:fix-windows-bugs-828

Conversation

@yutaro-sakamoto

Copy link
Copy Markdown
Contributor

Note / 注記: この内容はフォーク側リポジトリ yutaro-sakamoto/opensourcecobol4j の main ブランチに PR #13 としてマージ済みです。 / This change set is already merged into the main branch of the fork yutaro-sakamoto/opensourcecobol4j via PR #13.

概要

#828 とその sub issue(#829#835)で報告された、Windows 固有の cobj.exe の不具合を修正します。

修正内容

1. MSVC ビルドでの segfault 5 件(#829, #831, #832, #833, #834)

根本原因は 1 つでした。cobj/codegen.c に、C コードを生成していた opensource-cobol 由来の inside_check/inside_stack 機構が残っており、Java コード生成への書き換え時にデクリメントだけが生き残っていました(インクリメント箇所が存在しない)。この機構は全て GCC 以外でのみコンパイルされるため、MSVC ビルドの cobj.exe だけが -debug + 変数添字・参照変更・SEARCH ALL の組み合わせでカウンタが負になり、inside_stack[-2] への範囲外書き込みで segfault していました。生成される Java はビルドに使った C コンパイラに依存すべきではないため、機構ごと削除しました(GCC ビルドの出力・動作は不変)。

2. -java-package 使用時の不正な jar パス(#830)

jar コマンドの引数が「/ 区切りのパッケージパス + Windows では \file_path_delimitor」で連結され、com/abc\prog.class のような混在パスが生成されていました。jar は全プラットフォームで / を受け付けるため / に統一。逆に cmd.exe の del/ を受け付けないため、削除コマンドはネイティブセパレータ + cd 方式に変更し、del が引数として解釈してしまう残留トークン #aaa も削除しました。

3. 大文字小文字のみ異なる PROGRAM-ID(#835)

各プログラムはプログラム ID 名の .java/.class として生成されるため、大文字小文字のみ異なる ID(例: PROGprog)はケース非区別ファイルシステム上で互いのファイルを上書きします。これは原理的に共存不可能なため、Windows ではコード生成前に衝突を検出して明確なコンパイルエラーを報告するようにしました(従来はサイレントに壊れた成果物が生成されていました)。該当 2 テストは「FS の原理的制約」としてスキップを継続し(コメントを正確な説明に更新)、診断を検証する Windows 専用テストを新設しました。

4. その他の堅牢化

  • Windows の cd はドライブを跨げないため cd /d を使用(-o が別ドライブを指す場合の誤動作防止)
  • -java-package の長い引数によるスタックバッファオーバーフローを解消(package_name_to_path に出力サイズ引数を追加、strcpysnprintf)
  • cppcheck の指摘(variableScope)対応

検証

  • スキップされていた Windows テスト 9 件(-debug-jar, -single-jar and -oSEARCH KEY IN RHS、添字範囲外 MOVE ×2、参照変更 ×3、変数参照変更 MOVE)の AT_SKIP_IF を削除し、フォークの Windows CI で全て pass
  • 新設の Windows 専用テスト(ケース衝突の診断)も pass
  • 全ジョブ green の CI run: https://github.com/yutaro-sakamoto/opensourcecobol4j/actions/runs/30202068611
  • Linux では動作不変(削除コードは GCC では元々コンパイル対象外、新チェックは #ifdef _WIN32)

既知のフォローアップ候補(別 issue 相当)

  • ケース衝突チェックは翻訳単位内のみ(複数入力ファイル間は未検出)
  • 完全同名の PROGRAM-ID は全プラットフォームでサイレントに上書きされる既存問題
  • macOS(APFS)もケース非区別だが現状 _WIN32 のみ対象

Fixes #829
Fixes #830
Fixes #831
Fixes #832
Fixes #833
Fixes #834
Fixes #835
Part of #828


Summary

Fixes the Windows-specific cobj.exe bugs reported in #828 and its sub-issues (#829-#835).

Changes

1. Five MSVC segfaults (#829, #831, #832, #833, #834)

One root cause: cobj/codegen.c kept the inside_check/inside_stack bookkeeping inherited from opensource-cobol's C code generator, but only the decrements survived the rewrite to Java code generation — nothing ever increments the counter. The blocks only compile on non-GCC builds, so the MSVC-built cobj.exe alone drove the counter negative whenever a runtime check was attached (-debug with a variable subscript, reference modification, or SEARCH ALL) and wrote out of bounds via inside_stack[-2]. The machinery is removed entirely; generated Java must not depend on the compiler that built cobj, and GCC builds are unaffected.

2. Malformed jar paths with -java-package (#830)

The jar arguments mixed '/' from the package path with '\' from file_path_delimitor on Windows (e.g. com/abc\prog.class). The jar tool accepts '/' everywhere, so the paths are now joined with '/' unconditionally. The cleanup command had the opposite problem — cmd.exe's del rejects '/' — so it now uses native separators via cd, and a leftover #aaa token was removed.

3. PROGRAM-IDs differing only in letter case (#835)

Each program becomes its own .java/.class named after the program ID, so IDs differing only in case overwrite each other's files on a case-insensitive filesystem. This cannot work by design, so on Windows cobj now detects the collision before code generation and fails with an explicit error instead of silently producing broken output. The two affected tests remain skipped on Windows with accurate comments, and a new Windows-only test verifies the diagnostic.

4. Additional hardening

  • Use cd /d on Windows so the jar/del commands work when -o points at another drive
  • Fix a stack buffer overflow with overlong -java-package arguments (package_name_to_path now takes an output bound; strcpy replaced with snprintf)
  • Address a cppcheck variableScope finding

Verification

Known follow-up candidates

  • The case-collision check covers one translation unit only (not across multiple input files)
  • Programs with exactly identical PROGRAM-IDs still silently overwrite each other on every platform (pre-existing)
  • macOS (APFS) is also case-insensitive by default but is not covered by the _WIN32 predicate

codegen.c kept the inside_check/inside_stack bookkeeping from the
original C code generator, but only the decrements survived the
rewrite to Java code generation: nothing ever increments inside_check.
On non-GCC builds (the MSVC-built cobj.exe) the counter goes negative
as soon as a runtime check is attached to a reference (-debug with a
variable subscript, reference modification or SEARCH ALL), and
inside_stack[inside_check - 1] then writes out of bounds, crashing the
compiler. GCC builds never compile these blocks, which is why Linux
was unaffected. Remove the machinery entirely: the generated Java
must not depend on the compiler that built cobj.

Un-skip the Windows tests that were failing due to this crash.

Fixes opensourcecobol#829, opensourcecobol#831, opensourcecobol#832, opensourcecobol#833, opensourcecobol#834 (sub-issues of opensourcecobol#828)
With -java-package the jar arguments were assembled as
package_dir + file_path_delimitor + class, mixing the '/' separators
from the package path with '\' on Windows (e.g. com/abc\prog.class),
which produces malformed entry names in the archive. The jar tool
accepts '/' everywhere, so join the paths with '/' unconditionally.

The class file cleanup after archiving had the opposite problem:
cmd.exe's del does not accept '/' separators. Build the remove paths
with the native separator, let cd handle the output directory, and
drop a leftover '#aaa' token that del would treat as an argument.

Un-skip the Windows jar test.

Fixes opensourcecobol#830 (sub-issue of opensourcecobol#828)
Removing the inside_check block exposed a variableScope finding for
'code' in joutput_stmt; declare it in the handler block where it is
used.
Every program in a translation unit becomes its own .java/.class file
named after the program ID, so two programs whose IDs differ only in
letter case overwrite each other's files on a case-insensitive
filesystem and the result silently misbehaves. On Windows, detect the
collision before code generation and fail with an explicit error.

The two affected tests stay skipped on Windows because the behavior
they exercise is impossible there by design; a new Windows-only test
verifies the diagnostic instead.

Fixes opensourcecobol#835 (sub-issue of opensourcecobol#828)
On case-insensitive file systems programs whose PROGRAM-IDs differ
only in letter case would generate class files with the same name;
cobj now reports a compile error there, so document the restriction
in the usage section.
* Use "cd /d" on Windows: cmd.exe's plain "cd" does not switch
  drives, so with -o pointing at another drive the jar and del
  commands would run against the wrong directory
* Replace the unbounded strcpy into package_dir_native with snprintf
  and give package_name_to_path an explicit output bound, so an
  overlong -java-package argument can no longer overflow the buffers
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment