Skip to content

Fix CAN-FD signal correctness and channel lifecycle; release 4.3.2 - #166

Open
sebi2k1 wants to merge 5 commits into
masterfrom
fix/correctness-group-1
Open

sebi2k1 wants to merge 5 commits into
masterfrom
fix/correctness-group-1

Conversation

@sebi2k1

@sebi2k1 sebi2k1 commented May 31, 2026

Copy link
Copy Markdown
Owner

Summary

Fix signal corruption and channel teardown bugs, and bump the patch version to 4.3.2.

  • Encode and decode CAN-FD signals from the correct byte window, including signals beyond byte 7 and unaligned signals that span more than eight bytes.
  • Reject invalid offsets before integer conversion, validate effective signal widths, and enforce overflow-safe frame and buffer bounds.
  • Reconstruct decoded high words correctly in JavaScript and honor minimum and maximum signal bounds of zero.
  • Stop reader threads before closing sockets, release initialized synchronization resources, check thread creation results, and prevent duplicate stop cleanup. Reset the stop guard on each start so restarted channels can stop and release their handles.
  • Add regression coverage for invalid offsets, buffer boundaries, high words, zero-valued bounds, frame overflow, and repeated channel start/stop cycles with natural process exit.

Includes the CAN-FD and lifecycle fixes from the dependent branches. Master is merged into this branch; the remaining diff contains the fixes, tests, changelog, and version bump.

Type of change

  • Bug fix
  • New feature
  • Refactor / cleanup
  • Documentation
  • CI / tooling

Validation

The final file contents match the version validated with Apple Container on Linux ARM64, Node.js 22.23.2, and the custom SocketCAN-enabled kernel 6.18.5-cz-7800b4642171:

  • Native and TypeScript builds passed.
  • ESLint passed without modifying source files.
  • All 97 tests passed with vcan0/vcan1 and NODE_CAN_RUN_PRIVILEGED_TESTS=1, including interface recovery/removal and restart cleanup.

Final commit e238305 passed all 12 push and PR build/test jobs across Node.js 22.22.2, 24.15.0, and 26.x on x64 and ARM64. CodeQL analysis for Actions, C/C++, and JavaScript/TypeScript and the aggregate CodeQL check also passed.

Checklist

  • pnpm run build:all passes
  • Lint passes
  • Tests added or updated for changed behaviour
  • pnpm test passes with virtual CAN interfaces
  • CHANGELOG.md updated

Four small, surgical fixes for silent-data-corruption and UB bugs
identified in the full code review:

- socketcan.ts: replace `x << 32` (which is `x << 0` in JS) with
  `x * TWO_TO_32` when reassembling the high word of decoded signals.
  The old expression silently dropped the high 32 bits for any signal
  wider than 32 bits.
- socketcan.ts: switch `min/maxValue` bounds checks from truthy to
  `!= null` so that a legitimate bound of `0` is honoured.
- can.cc: reject Send() payloads larger than CAN_MAX_DLEN before the
  memcpy into the 8-byte `struct can_frame.data`, preventing a stack
  overwrite on caller error.
- signals.cc: validate that `bitLength` is in the range 1..64 in both
  DecodeSignal and EncodeSignal. Out-of-range values previously caused
  shifts of full type width / by negative amounts (UB).

Tests:
- test-signal_conversion.js: high-word regression case plus bitLength
  range validation for encode and decode.
- test-signal_bounds.js: new file covering the `0`-boundary cases for
  Signal min/maxValue.
- test-send_overflow.js: new file asserting Send() throws on oversized
  buffers (skips when vcan0 is unavailable).

Also includes ESLint auto-fix formatting touched by `npm run lint`
(trailing commas, switch reformatting) across `parse_kcd.ts`,
`can.d.ts`, `can_signals.d.ts`, and `socketcan.ts`.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The previous _getvalue / _setvalue always memcpy'd bytes 0..7 of the
local buffer into a uint64_t and shifted from there. That assumption
predates CAN-FD support: for any signal whose bit offset lies past
byte 7 of a 64-byte CAN-FD payload, the routine returned garbage or
hit shift-by-full-type-width UB (Intel offset >= 64) / size_t
underflow on the Motorola shift formula 64 - offset - length
(offset > 64). Encoding had the symmetric bug.

Rewrite both helpers as a windowed fast path plus a per-bit fallback:

- Fast path (essentially all real signals): memcpy 8 bytes starting at
  data[offset/8], endian-swap, then shift by (offset % 8) for Intel or
  (64 - bit_in_byte - length) for Motorola, then mask. Same shape and
  cost as the previous code, just with the byte_start factored out.

- Fallback: when bit_in_byte + length > 64 — a 58..64 bit signal at a
  non-byte-aligned offset, essentially never seen in real CAN
  databases — walk the signal bit by bit. Both endiannesses handled.

The local scratch buffer grows from 64 to 72 bytes (MAX_PAYLOAD_BYTES
+ WINDOW_SAFETY_PAD = 8) so the 8-byte windowed memcpy is always
in-bounds even for a signal sitting at byte 63. The padding stays
zero and any bits read from it are masked off.

EncodeSignal now also memsets the local buffer before the memcpy-in.
The fast-path RMW may touch bytes past the caller's payload inside
the scratch buffer, and previously those bytes were uninitialised
stack memory — harmless because they're never written back, but
flagged as UB by the model.

Two new bounds checks are added in DecodeSignal and EncodeSignal:
  - offset + effectiveBitLength must fit in MAX_TOTAL_BITS (512).
  - signal must fit within the caller's JS buffer length.
Both throw TypeError with a clear message rather than silently
producing wrong results.

The KAYAK_DATA_CHECK debug blocks are removed: the per-bit walk that
they referenced is now the production fallback path, so the
self-comparison is no longer meaningful.

Tests cover all the worked examples plus the boundaries:
- Intel  16-bit at offset 200 (decode + encode, RMW preserves neighbours)
- Motorola 12-bit at byte-aligned offset 144 (decode + encode)
- Motorola 10-bit at non-byte-aligned offset 147 (round-trip)
- 8-bit at byte 63 (last byte of CAN-FD payload)
- 64-bit Intel at offsets 0 and 64 (length-64 boundary)
- 58-bit Intel at offset 7 (exercises the per-bit fallback)
- All four out-of-bounds throws (frame and buffer).

All 13 new cases plus the 27 pre-existing signal tests pass in the
node:22-bookworm-slim Docker container.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
sebi2k1 and others added 2 commits September 6, 2026 09:24
* Bump @typescript-eslint/parser from 8.60.0 to 8.60.1

Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 8.60.0 to 8.60.1.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.60.1/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.60.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump @typescript-eslint/eslint-plugin from 8.60.0 to 8.60.1

Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 8.60.0 to 8.60.1.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.60.1/packages/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.60.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump @types/node from 25.9.1 to 25.9.2

Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 25.9.1 to 25.9.2.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 25.9.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump node-gyp from 12.3.0 to 12.4.0

Bumps [node-gyp](https://github.com/nodejs/node-gyp) from 12.3.0 to 12.4.0.
- [Release notes](https://github.com/nodejs/node-gyp/releases)
- [Changelog](https://github.com/nodejs/node-gyp/blob/main/CHANGELOG.md)
- [Commits](nodejs/node-gyp@v12.3.0...v12.4.0)

---
updated-dependencies:
- dependency-name: node-gyp
  dependency-version: 12.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump node-gyp from 12.4.0 to 13.0.0

Bumps [node-gyp](https://github.com/nodejs/node-gyp) from 12.4.0 to 13.0.0.
- [Release notes](https://github.com/nodejs/node-gyp/releases)
- [Changelog](https://github.com/nodejs/node-gyp/blob/main/CHANGELOG.md)
- [Commits](nodejs/node-gyp@v12.4.0...v13.0.0)

---
updated-dependencies:
- dependency-name: node-gyp
  dependency-version: 13.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump @typescript-eslint/eslint-plugin from 8.60.1 to 8.61.0

Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 8.60.1 to 8.61.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.61.0/packages/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.61.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump @types/node from 25.9.2 to 25.9.3

Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 25.9.2 to 25.9.3.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 25.9.3
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump prettier from 3.8.3 to 3.8.4

Bumps [prettier](https://github.com/prettier/prettier) from 3.8.3 to 3.8.4.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](prettier/prettier@3.8.3...3.8.4)

---
updated-dependencies:
- dependency-name: prettier
  dependency-version: 3.8.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump eslint from 10.4.1 to 10.5.0

Bumps [eslint](https://github.com/eslint/eslint) from 10.4.1 to 10.5.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](eslint/eslint@v10.4.1...v10.5.0)

---
updated-dependencies:
- dependency-name: eslint
  dependency-version: 10.5.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump version to 4.2.2

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Bump @typescript-eslint/eslint-plugin from 8.61.0 to 8.61.1

Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 8.61.0 to 8.61.1.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.61.1/packages/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.61.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump @types/node from 25.9.3 to 26.0.0

Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 25.9.3 to 26.0.0.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 26.0.0
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump tar in the npm_and_yarn group across 1 directory

Bumps the npm_and_yarn group with 1 update in the / directory: [tar](https://github.com/isaacs/node-tar).


Updates `tar` from 7.5.15 to 7.5.16
- [Release notes](https://github.com/isaacs/node-tar/releases)
- [Changelog](https://github.com/isaacs/node-tar/blob/main/CHANGELOG.md)
- [Commits](isaacs/node-tar@v7.5.15...v7.5.16)

---
updated-dependencies:
- dependency-name: tar
  dependency-version: 7.5.16
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump @typescript-eslint/parser from 8.61.0 to 8.61.1

Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 8.61.0 to 8.61.1.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.61.1/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.61.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix(signals): add missing <endian.h> include for ARM64 build

le64toh/be64toh/htole64/htobe64 are declared in <endian.h>. The header
was being pulled in transitively on x86 but not on ARM64, so the build
failed there with "'le64toh' was not declared in this scope". Include it
explicitly. Verified to compile under -std=c++20 on glibc and musl.

Fixes #182

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* ci: build and test on ARM64 runners

Add ubuntu-24.04-arm to the matrix (with fail-fast: false) so the native
addon is validated on ARM64, the platform that regressed in #182.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Bump version to 4.2.3

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* Bump @typescript-eslint/eslint-plugin from 8.61.1 to 8.62.0

Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 8.61.1 to 8.62.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.62.0/packages/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.62.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump node-addon-api from 8.8.0 to 8.9.0

Bumps [node-addon-api](https://github.com/nodejs/node-addon-api) from 8.8.0 to 8.9.0.
- [Release notes](https://github.com/nodejs/node-addon-api/releases)
- [Changelog](https://github.com/nodejs/node-addon-api/blob/main/CHANGELOG.md)
- [Commits](nodejs/node-addon-api@v8.8.0...v8.9.0)

---
updated-dependencies:
- dependency-name: node-addon-api
  dependency-version: 8.9.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump prettier from 3.8.4 to 3.9.1

Bumps [prettier](https://github.com/prettier/prettier) from 3.8.4 to 3.9.1.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](prettier/prettier@3.8.4...3.9.1)

---
updated-dependencies:
- dependency-name: prettier
  dependency-version: 3.9.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump @types/node from 26.0.0 to 26.0.1

Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 26.0.0 to 26.0.1.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 26.0.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump eslint from 10.5.0 to 10.6.0

Bumps [eslint](https://github.com/eslint/eslint) from 10.5.0 to 10.6.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](eslint/eslint@v10.5.0...v10.6.0)

---
updated-dependencies:
- dependency-name: eslint
  dependency-version: 10.6.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump @types/node from 26.0.1 to 26.1.0

Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 26.0.1 to 26.1.0.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 26.1.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump prettier from 3.9.1 to 3.9.4

Bumps [prettier](https://github.com/prettier/prettier) from 3.9.1 to 3.9.4.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](prettier/prettier@3.9.1...3.9.4)

---
updated-dependencies:
- dependency-name: prettier
  dependency-version: 3.9.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump @typescript-eslint/parser from 8.62.0 to 8.62.1

Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 8.62.0 to 8.62.1.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.62.1/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.62.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump @typescript-eslint/eslint-plugin from 8.62.0 to 8.62.1

Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 8.62.0 to 8.62.1.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.62.1/packages/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.62.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump @types/node from 26.1.0 to 26.1.1

Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 26.1.0 to 26.1.1.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 26.1.1
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump @typescript-eslint/eslint-plugin from 8.62.1 to 8.64.0

Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 8.62.1 to 8.64.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.64.0/packages/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.64.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump globals from 17.6.0 to 17.7.0

Bumps [globals](https://github.com/sindresorhus/globals) from 17.6.0 to 17.7.0.
- [Release notes](https://github.com/sindresorhus/globals/releases)
- [Commits](sindresorhus/globals@v17.6.0...v17.7.0)

---
updated-dependencies:
- dependency-name: globals
  dependency-version: 17.7.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump node-gyp from 13.0.0 to 13.0.1

Bumps [node-gyp](https://github.com/nodejs/node-gyp) from 13.0.0 to 13.0.1.
- [Release notes](https://github.com/nodejs/node-gyp/releases)
- [Changelog](https://github.com/nodejs/node-gyp/blob/main/CHANGELOG.md)
- [Commits](nodejs/node-gyp@v13.0.0...v13.0.1)

---
updated-dependencies:
- dependency-name: node-gyp
  dependency-version: 13.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* chore: upgrade dependencies, bump to v4.2.4 (#201)

- Bump @types/node, @typescript-eslint/*, eslint, globals, node-addon-api,
  node-gyp, prettier to latest within existing semver ranges
- Pin brace-expansion to ^5.0.8 via overrides to fix high-severity DoS
  (GHSA-mh99-v99m-4gvg) pulled in transitively via mocha's glob/minimatch,
  following the same pattern used for diff/serialize-javascript/uuid
- Reformat src/*.ts per updated prettier/eslint defaults (npm run lint --fix)
- Leave typescript on ^6.0.0 (6.0.3): 7.0.2 is a same-day major release,
  already tracked separately by the open dependabot/npm_and_yarn/typescript-7.0.2 PR

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>

* chore: sync package-lock.json version to 4.2.4

PR #201 bumped package.json to 4.2.4 but left the lockfile's version
fields at 4.2.3.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix: bound CAN data length before memcpy in Send/SendFD (#199, #200) (#202)

* fix: bound CAN data length before memcpy in Send/SendFD (#199, #200)

RawChannel::Send and RawChannel::SendFD copied the caller-controlled JS
Buffer into a fixed-size stack field (frame.data[8] / frameFD.data[64])
using the Buffer's own byte length as the copy size, with no upper bound
before the memcpy. A data Buffer longer than the field overflowed it and
smashed adjacent stack memory (CWE-787). In SendFD the existing len > 64
clamp ran after the memcpy, so it never bounded the copy.

Reject over-length buffers with a thrown error before the copy, sized to
the actual struct field via sizeof(). Throwing rather than silently
truncating surfaces the caller's mistake instead of sending a malformed
frame.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* chore: bump version to 4.2.5

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

* Bump @types/node from 26.1.1 to 26.1.2 (#205)

Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 26.1.1 to 26.1.2.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 26.1.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump globals from 17.7.0 to 17.9.0 (#208)

Bumps [globals](https://github.com/sindresorhus/globals) from 17.7.0 to 17.9.0.
- [Release notes](https://github.com/sindresorhus/globals/releases)
- [Commits](sindresorhus/globals@v17.7.0...v17.9.0)

---
updated-dependencies:
- dependency-name: globals
  dependency-version: 17.9.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump @typescript-eslint/eslint-plugin from 8.65.0 to 8.66.0 (#206)

Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 8.65.0 to 8.66.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.66.0/packages/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.66.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump mocha from 11.7.6 to 11.8.0 (#207)

Bumps [mocha](https://github.com/mochajs/mocha) from 11.7.6 to 11.8.0.
- [Release notes](https://github.com/mochajs/mocha/releases)
- [Changelog](https://github.com/mochajs/mocha/blob/v11.8.0/CHANGELOG.md)
- [Commits](mochajs/mocha@v11.7.6...v11.8.0)

---
updated-dependencies:
- dependency-name: mocha
  dependency-version: 11.8.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore: update dependencies and migrate to pnpm (#209)

* chore: bump version to 4.3.0

* ci: use npm trusted publishing (#210)

* Bump node-gyp from 13.0.1 to 13.0.2 (#214)

Bumps [node-gyp](https://github.com/nodejs/node-gyp) from 13.0.1 to 13.0.2.
- [Release notes](https://github.com/nodejs/node-gyp/releases)
- [Changelog](https://github.com/nodejs/node-gyp/blob/main/CHANGELOG.md)
- [Commits](nodejs/node-gyp@v13.0.1...v13.0.2)

---
updated-dependencies:
- dependency-name: node-gyp
  dependency-version: 13.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump @types/node from 26.2.0 to 26.4.0 (#213)

Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 26.2.0 to 26.4.0.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 26.4.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump eslint from 10.8.1 to 10.9.1 (#212)

Bumps [eslint](https://github.com/eslint/eslint) from 10.8.1 to 10.9.1.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](eslint/eslint@v10.8.1...v10.9.1)

---
updated-dependencies:
- dependency-name: eslint
  dependency-version: 10.9.1
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump @typescript-eslint/parser from 8.67.0 to 8.68.0 (#211)

Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 8.67.0 to 8.68.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.68.0/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.68.0
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* fix: recover RawChannel from transient POLLERR events (#216)

* fix: recover RawChannel from transient poll errors

* chore: bump version to 4.3.1

* test: cover RawChannel interface lifecycle

* test: expect channel recovery while interface is down

* Fix correctness bugs in signal decoding and frame sending

Four small, surgical fixes for silent-data-corruption and UB bugs
identified in the full code review:

- socketcan.ts: replace `x << 32` (which is `x << 0` in JS) with
  `x * TWO_TO_32` when reassembling the high word of decoded signals.
  The old expression silently dropped the high 32 bits for any signal
  wider than 32 bits.
- socketcan.ts: switch `min/maxValue` bounds checks from truthy to
  `!= null` so that a legitimate bound of `0` is honoured.
- can.cc: reject Send() payloads larger than CAN_MAX_DLEN before the
  memcpy into the 8-byte `struct can_frame.data`, preventing a stack
  overwrite on caller error.
- signals.cc: validate that `bitLength` is in the range 1..64 in both
  DecodeSignal and EncodeSignal. Out-of-range values previously caused
  shifts of full type width / by negative amounts (UB).

Tests:
- test-signal_conversion.js: high-word regression case plus bitLength
  range validation for encode and decode.
- test-signal_bounds.js: new file covering the `0`-boundary cases for
  Signal min/maxValue.
- test-send_overflow.js: new file asserting Send() throws on oversized
  buffers (skips when vcan0 is unavailable).

Also includes ESLint auto-fix formatting touched by `npm run lint`
(trailing commas, switch reformatting) across `parse_kcd.ts`,
`can.d.ts`, `can_signals.d.ts`, and `socketcan.ts`.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* Fix CAN-FD bit extraction for signals past byte 7

The previous _getvalue / _setvalue always memcpy'd bytes 0..7 of the
local buffer into a uint64_t and shifted from there. That assumption
predates CAN-FD support: for any signal whose bit offset lies past
byte 7 of a 64-byte CAN-FD payload, the routine returned garbage or
hit shift-by-full-type-width UB (Intel offset >= 64) / size_t
underflow on the Motorola shift formula 64 - offset - length
(offset > 64). Encoding had the symmetric bug.

Rewrite both helpers as a windowed fast path plus a per-bit fallback:

- Fast path (essentially all real signals): memcpy 8 bytes starting at
  data[offset/8], endian-swap, then shift by (offset % 8) for Intel or
  (64 - bit_in_byte - length) for Motorola, then mask. Same shape and
  cost as the previous code, just with the byte_start factored out.

- Fallback: when bit_in_byte + length > 64 — a 58..64 bit signal at a
  non-byte-aligned offset, essentially never seen in real CAN
  databases — walk the signal bit by bit. Both endiannesses handled.

The local scratch buffer grows from 64 to 72 bytes (MAX_PAYLOAD_BYTES
+ WINDOW_SAFETY_PAD = 8) so the 8-byte windowed memcpy is always
in-bounds even for a signal sitting at byte 63. The padding stays
zero and any bits read from it are masked off.

EncodeSignal now also memsets the local buffer before the memcpy-in.
The fast-path RMW may touch bytes past the caller's payload inside
the scratch buffer, and previously those bytes were uninitialised
stack memory — harmless because they're never written back, but
flagged as UB by the model.

Two new bounds checks are added in DecodeSignal and EncodeSignal:
  - offset + effectiveBitLength must fit in MAX_TOTAL_BITS (512).
  - signal must fit within the caller's JS buffer length.
Both throw TypeError with a clear message rather than silently
producing wrong results.

The KAYAK_DATA_CHECK debug blocks are removed: the per-bit walk that
they referenced is now the production fallback path, so the
self-comparison is no longer meaningful.

Tests cover all the worked examples plus the boundaries:
- Intel  16-bit at offset 200 (decode + encode, RMW preserves neighbours)
- Motorola 12-bit at byte-aligned offset 144 (decode + encode)
- Motorola 10-bit at non-byte-aligned offset 147 (round-trip)
- 8-bit at byte 63 (last byte of CAN-FD payload)
- 64-bit Intel at offsets 0 and 64 (length-64 boundary)
- 58-bit Intel at offset 7 (exercises the per-bit fallback)
- All four out-of-bounds throws (frame and buffer).

All 13 new cases plus the 27 pre-existing signal tests pass in the
node:22-bookworm-slim Docker container.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* Fix lifecycle / teardown races in RawChannel

Four small correctness fixes in native/can.cc:

- ~RawChannel closed m_SocketFd BEFORE joining the reader thread,
  while the reader is mid-poll() on that fd. Closing an fd from one
  thread while another is in poll() on it is POSIX-undefined — the
  fd can be reused by an unrelated open() before poll() returns.
  Reorder: stopThread() first, then close(m_SocketFd).

- async_channel_stopped() could run twice — once from JS Stop() and
  once from the reader-thread uv_async_send issued on POLLHUP/POLLERR.
  The second invocation would re-run the listener loop, schedule a
  double uv_close (libuv assertion), and call Unref() one too many
  times. Add an m_StoppedAlready single-shot guard.

- pthread_create's return value was ignored. The subsequent
  CHECK_CONDITION(m_Thread, ...) checked the output pthread_t handle,
  which is an opaque type that may legitimately be zero on some
  implementations. Capture rc and check rc == 0 instead.

- pthread_mutex_init / pthread_cond_init were paired with nothing
  in the destructor. Add pthread_*_destroy calls in ~RawChannel,
  gated by a new m_SyncInitialized flag (the sync primitives are
  only initialised on the successful constructor path, after a
  successful bind()).

These are review-validated fixes; the races (and the Unref ordering)
are hard or impossible to surface from a mocha test without thread-
sanitizer instrumentation or kernel-level cooperation. No behavioural
test is shipped — the Docker build-test passes 47/57 (same as master;
the 10 failures are all pre-existing 'Error while creating channel'
caused by missing vcan0/vcan1 in the build image).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix: validate signal offsets and reset restart cleanup; bump to 4.3.2

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
@sebi2k1 sebi2k1 changed the title Fix correctness bugs in signal decoding and frame sending Fix CAN-FD signal correctness and channel lifecycle; release 4.3.2 Sep 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant