fix(driver-ablegacy): resolve Medium code-review finding (Driver.AbLegacy-004)

DecodeValue for Bit with no bitIndex now reads the full 16-bit word via
GetInt16(0) and tests bit 0 instead of GetInt8(0), which only covered the
low byte and silently misread any bit in positions 8..15. The comment
explains the two decode paths (suffix-present vs suffix-absent).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Joseph Doherty
2026-05-22 09:24:19 -04:00
parent 7d30009dc8
commit 47eac2d84f
2 changed files with 7 additions and 3 deletions

View File

@@ -117,7 +117,7 @@ reject file numbers on I/O/S, and restrict which file letters may carry a sub-el
| Severity | Medium | | Severity | Medium |
| Category | Correctness & logic bugs | | Category | Correctness & logic bugs |
| Location | `LibplctagLegacyTagRuntime.cs:36-37` | | Location | `LibplctagLegacyTagRuntime.cs:36-37` |
| Status | Open | | Status | Resolved |
**Description:** `DecodeValue` for `AbLegacyDataType.Bit` with `bitIndex == null` **Description:** `DecodeValue` for `AbLegacyDataType.Bit` with `bitIndex == null`
returns `_tag.GetInt8(0) != 0`. A bit-file element (`B3:0/0`) is a single bit inside returns `_tag.GetInt8(0) != 0`. A bit-file element (`B3:0/0`) is a single bit inside
@@ -132,7 +132,7 @@ but a `Bit`-typed tag configured with an address that has no `/bit` suffix (e.g.
bit suffix on `Bit`-typed tags (validate in `CreateInstance`/`DiscoverAsync`) or bit suffix on `Bit`-typed tags (validate in `CreateInstance`/`DiscoverAsync`) or
decode the full 16-bit word and test bit 0. decode the full 16-bit word and test bit 0.
**Resolution:** _(open)_ **Resolution:** Resolved 2026-05-22 — `DecodeValue` for `Bit` with no `bitIndex` now reads the full 16-bit word via `GetInt16(0)` and tests bit 0, avoiding the silent half-word truncation from `GetInt8`.
### Driver.AbLegacy-005 ### Driver.AbLegacy-005

View File

@@ -33,9 +33,13 @@ internal sealed class LibplctagLegacyTagRuntime : IAbLegacyTagRuntime
public object? DecodeValue(AbLegacyDataType type, int? bitIndex) => type switch public object? DecodeValue(AbLegacyDataType type, int? bitIndex) => type switch
{ {
// When a bit suffix is present (e.g. B3:0/5) libplctag resolves the individual bit and
// GetBit returns it directly. When there is no suffix the caller addressed a Bit-typed
// tag without an explicit bit index; read the full 16-bit word and test bit 0 — GetInt8
// only covers the low byte and silently misses any bit set in bits 8..15.
AbLegacyDataType.Bit => bitIndex is int bit AbLegacyDataType.Bit => bitIndex is int bit
? _tag.GetBit(bit) ? _tag.GetBit(bit)
: _tag.GetInt8(0) != 0, : (_tag.GetInt16(0) & 1) != 0,
AbLegacyDataType.Int or AbLegacyDataType.AnalogInt => (int)_tag.GetInt16(0), AbLegacyDataType.Int or AbLegacyDataType.AnalogInt => (int)_tag.GetInt16(0),
AbLegacyDataType.Long => _tag.GetInt32(0), AbLegacyDataType.Long => _tag.GetInt32(0),
AbLegacyDataType.Float => _tag.GetFloat32(0), AbLegacyDataType.Float => _tag.GetFloat32(0),