Skip to content

lin::ldf::DB::decode() has an unclamped bit-shift (UB, aborts under UBSan) for LDF signals with bit_width > 64 #18

Open

Description

@SoundMatt

What's wrong

Signal::bit_width is a plain int (include/lin/ldf/parser.hpp:46), and parse_signals() (src/ldf/parser.cpp:195) accepts any integer bit_width parsed from LDF text with no upper-bound check.

decode() (src/ldf/parser.cpp:51-74) then loops for (int i = 0; i < bit_width; ++i) ... val |= (uint64_t)1 << i with no clamp on i (line 69). If bit_width > 64, this is a shift-by->=64 on a 64-bit value, which is undefined behavior in C++.

Reproduced directly: a standalone UBSan-compiled repro confirms 1ULL << 64 triggers shift exponent 64 is too large for 64-bit type and SIGABRTs — matching the repo's own fusa-asil-b / UBSan CI job, which runs with halt_on_error=1 and would abort on this input.

Reaching the actual UB additionally requires decode()'s data argument to exceed 8 bytes (otherwise the loop's own byte_idx >= data.size() break fires first) — but decode() takes an arbitrary, unbounded std::vector<uint8_t>& with no internal length check, so this precondition is realistic via the public API, not just a theoretical corner case.

Evidence

  • include/lin/ldf/parser.hpp:46 — Signal::bit_width is unconstrained int
  • src/ldf/parser.cpp:195 — parse_signals(), no bound check on parsed bit_width
  • src/ldf/parser.cpp:51-74 — decode(), unclamped shift at line 69
  • tests/test_ldf.cpp:113 — the only bit_width assertion in the suite (bit_width == 8); no coverage of out-of-range values

Why it matters

A malformed or crafted .ldf file (an LDF is typically an external, semi-trusted config/description file supplied alongside a project) can crash any process that parses it and calls decode() on a >8-byte buffer. This is a straightforward DoS via untrusted input, and it also means the safety-relevant UBSan CI gate can be defeated/triggered by config content rather than code changes.

Suggested fix direction

Reject bit_width values outside [1, 64] (or the LIN-spec-appropriate range) in parse_signals() at LDF-parse time, and/or clamp the loop bound in decode() to std::min(bit_width, 64) as defense in depth.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions